/**
 * 
 */
package tcss543.cpap.data;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;

import tcss543.cpap.common.ReviewerRank;
import tcss543.cpap.common.Utilities;

/**
 * This class is responsible for generating "sets" of test data for the CPAP
 * problem as defined in our project.
 * 
 * Note: ReviewerID, PaperId, KeywordID all start at 1 (not 0)
 * 
 * @author ashish
 * 
 */
public class DataGenerator {

	/**
	 * no. of papers
	 */
	private final short noOfPapers;

	/**
	 * no of reviewers
	 */
	private final short noOfReviewers;

	/**
	 * Reviewers is a map of the reviewerid (1..) to a map that maps each
	 * keyword to their ranking
	 */
	private final Map<Short, Map<Short, ReviewerRank>> reviewers;

	/**
	 * Papers is a map of each paperid to a set of keywordid's
	 */
	private final Map<Short, Set<Short>> papers;
	private final Random paperKeyWordCountRandomizer;
	private final Random reviewerRandomizer;
	private final Random paperKeywordRandomizer;
	private final DataSetSkewType skew;

	/**
	 * Create the data generator with the specified number of authors and
	 * reviewers
	 * 
	 * @param p
	 *            : no. of papers in terms of PaperSetSize (forces standardized
	 *            data sets)
	 * @param r
	 *            : no. of reviewers
	 */
	public DataGenerator(PaperSetSize p, DataSetSkewType s) {
		noOfPapers = (short)p.size();
		// According to our constraint we actually need 3/4 reviewers per paper. 
		// However I am keeping them equal, this meets our constraint. 
		noOfReviewers = noOfPapers;
		
		reviewers = new HashMap<Short, Map<Short, ReviewerRank>>();
		papers = new HashMap<Short, Set<Short>>();

		// I use seeds so we can regenerate same data set
		paperKeyWordCountRandomizer = new Random(989l);
		paperKeywordRandomizer = new Random(5552332l);
		reviewerRandomizer = new Random(2938747834l);
		skew = s;
		createDataSet();
	}

	/**
	 * Creates the data set randomly.
	 */
	private void createDataSet() {
		// allocate memory for reviewers
		for (short i = 1; i <= noOfReviewers; i++)
			reviewers.put(i, new HashMap<Short, ReviewerRank>());

		// allocate memory for papers
		for (short i = 1; i <= noOfPapers; i++)
			papers.put(i, new HashSet<Short>());

		// assign a distinct set of keywords, randomly, to each paper
		for (short p = 1; p <= noOfPapers; p++) {
			// we need 1 to 20
			short setSize = (short) (paperKeyWordCountRandomizer.nextInt(20) + 1);
			// first we randomly pick how many keywords this paper has
			for (short j = 1; j <= setSize; j++) {
				// now we randomly pick the actual keywords. These have to be
				// distinct
				short kNext = -1;
				do {
					kNext = (short)(paperKeywordRandomizer.nextInt(20));
					kNext += 1;
				} while (papers.get(p).contains(kNext));
				papers.get(p).add(kNext);
			}
		}

		// randomly rank each keywrod for each reviewer based on skew
		for (short r = 1; r <= noOfReviewers; r++) {
			for (short i = 1; i <= 20; i++) {

				if (skew == DataSetSkewType.RANDOM) {
					reviewers.get(r).put(i,
							ReviewerRank.get(reviewerRandomizer.nextInt(3)));
				} else if (skew == DataSetSkewType.FIFTY_PERCENT_KEYWORDS_ARE_A_NO) {
					// the contract for at least 50% is no is a lower bound
					if (reviewerRandomizer.nextBoolean()) {
						// always no
						reviewers.get(r)
								.put(
										i,
										ReviewerRank.get(reviewerRandomizer
												.nextInt(2)));
					} else {
						// this one is still random from all three choices
						reviewers.get(r)
								.put(
										i,
										ReviewerRank.get(reviewerRandomizer
												.nextInt(3)));
					}
				} else if (skew == DataSetSkewType.ALL_YES) {
					reviewers.get(r).put(i, ReviewerRank.get(0));
				} else if (skew == DataSetSkewType.ALL_MAYBE) {
					reviewers.get(r).put(i, ReviewerRank.get(1));
				} else {
					throw new RuntimeException("Invalid skew type");
				}
			}

		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {

		StringBuilder sb = new StringBuilder();
		sb.append("Papers...\n");

		for (short p : papers.keySet()) {
			sb.append("Paper id = " + p);
			sb.append("\tkeywords assigned = ");
			for (short j : papers.get(p)) {
				sb.append(j);
				sb.append(" ");
			}
			sb.append("\n");
		}

		sb.append("\n\nReviewers...\n");

		for (short r : reviewers.keySet()) {
			sb.append("Reviewer id = " + r);
			sb.append("\tkeywords rating = ");
			for (short i = 1; i <= 20; i++) {
				sb.append(reviewers.get(r).get(i));
				sb.append(", ");

			}
			sb.append("\n");
		}
		return sb.toString();

	}

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void test_main(String[] args) throws IOException {
		System.out.println("Generating data set...");
		DataGenerator dg = new DataGenerator(PaperSetSize.SMALL,
				DataSetSkewType.FIFTY_PERCENT_KEYWORDS_ARE_A_NO);
		System.out.println("Data generated...");
		//System.in.read();
		System.out.println(dg.toString());
		
		
		//what would a solution look like?
		 Map<Short, Set<Short>> solution = new HashMap<Short, Set<Short>>();
		 short nextR = 1;
		 for(short p: dg.papers.keySet()){
			 
			 //randomly assign some reviewers o this paper
			 TreeSet<Short> a = new TreeSet<Short>();
			 
			 a.add(nextR++);
			 solution.put(p, a);
		 }
		 

		 System.out.println("Quality of solutin = " + Utilities.calculateQuality(solution, dg.getPapers(), dg.getReviewers(), false));
	}

	/**
	 * @return the reviewers
	 */
	public final Map<Short, Map<Short, ReviewerRank>> getReviewers() {
		return Collections.unmodifiableMap(reviewers);
	}

	/**
	 * @return the papers
	 */
	public final Map<Short, Set<Short>> getPapers() {
		return Collections.unmodifiableMap(papers);
	}

}
/*
 * 
 * we will fix (mu opt) = 3, and gamma min to 3 as well
 * 
 * 
 * Paper and reviewer will have an id Paper class that has a randomly generated
 * subset from the 20 keywords.
 * 
 * Each reviewer will randomly rate the 20 keywords except in skewed data where
 * we have all yes or all no
 * 
 * another function to calculate the Quality of a solution.
 */